
package frontend;
import java.io.*;
import java.lang.Exception;

public class Compiler
{
	private boolean verbose;
	private Parser parser;
	private SemanticAnalyzer sem;
	private CodeGenerator cg;
	private String javaCode;
	private String outputFolder;
	private String srcFileName;

	public static void printInstructions()
	{
		System.out.println("Usage: ");
		System.out.println("  java Compiler [-v] -d outputfolder <daytrader_source>");
		return;
	}
	
	public Compiler(String srcFileName, String outputFolder, String dataPathFileName, boolean verbose)
	{
		try {
			FileReader reader = new FileReader(srcFileName);
			parser = new Parser(reader);
		} catch (FileNotFoundException e) {
			System.err.print("***[DayTrader Compiler] Cannot find source file " + srcFileName);
			System.exit(1);
		}
		this.srcFileName = srcFileName;
		sem = new SemanticAnalyzer(dataPathFileName);
		cg = new CodeGenerator();
		this.outputFolder = outputFolder;
		this.verbose = verbose;
	}
	public void semanticAnalyze() throws CompileErrors
	{
		sem.analyze(parser.root);
		CodeOptimizer.optimize(parser.root);
		//parser.root.print(0, 15, verbose);
	}
	public void parse() throws CompileError
	{
		parser.yyparse();
	}
	public void compile() throws IOException
	{
		SyntaxTreeNode root = parser.root;
		assert (root.getType() == SyntaxTreeNode.Type.PROG);
		for (SyntaxTreeNode c: root.getChildren()) {
			genCode(c);
			dumpCode(c);
		}
	}

	public void genCode(SyntaxTreeNode rootNode) {
		switch (rootNode.getType())
		{
		case BUYSTRAT:
			javaCode = cg.genBuy(rootNode);
			break;
		case SELLSTRAT:
			javaCode = cg.genSell(rootNode);
			break;
		case USERDEFINE:
			javaCode = cg.genUser(rootNode);
			break;
		default:
			assert(false);
		}
		if (verbose)
	            System.out.print(javaCode);
	}

	public void dumpCode(SyntaxTreeNode rootNode) throws IOException{
		String fileName = "";
		switch (rootNode.getType())
		{
		case BUYSTRAT:
			fileName = "BuyStrategy.java";
			break;
		case SELLSTRAT:
			fileName = "SellStrategy.java";
			break;
		case USERDEFINE:
			fileName = "UserDefine.java";
			break;
		default:
			assert(false);
		}
		File folder = new File(outputFolder);
		File aFile = new File(folder, fileName);
		Writer output = new BufferedWriter(new FileWriter(aFile));
		try {
			output.write(javaCode);
		}
		finally {
			output.close();
		}
	}

	public static String parseArgOutputFolder(String [] args)
	{
		int i=0;
		String arg;
		for (i=0; i < args.length; i++) {
			arg = args[i];
			if (arg.equals("-d"))
				return args[i+1];
		}
		return ".";
	}

	public static String parseArgDataPathFileName(String [] args)
	{
		int i=0;
		String arg;
		for (i=0; i < args.length; i++) {
			arg = args[i];
			if (arg.equals("--data-path"))
				return args[i+1];
		}
		return null;
	}

	public static boolean parseArgVerbose(String [] args)
	{
		int i=0;
		String arg;
		for (i=0; i < args.length; i++) {
			arg = args[i];
			if (arg.equals("-v"))
				return true;
		}
		return false;
	}

	public void handleCompileError(CompileError e)
	{
		String errMsg = e.getMessage();
		errMsg = errMsg.substring(0,1).toUpperCase() + errMsg.substring(1);
		System.err.println("***[DayTrader Compiler] ");
		System.err.println("In the file: " + this.srcFileName + ", ");
		System.err.print(errMsg);
		System.err.println(" at line "+e.lineNumber);
	}
	public void handleCompileErrors(CompileErrors errs)
	{
		System.err.println("count: "+errs.size());
		for (CompileError e: errs.errs) {
			handleCompileError(e);
		}
	}

	public static void main (String [] args) throws IOException
	{
		if ( args.length == 0) {
			printInstructions();
			System.exit(0);
		}
		Compiler compiler;
		String outputFolder = Compiler.parseArgOutputFolder(args);
		String dataPathFileName = Compiler.parseArgDataPathFileName(args);
		boolean verbose = Compiler.parseArgVerbose(args);
		compiler = new Compiler(args[args.length - 1], outputFolder, dataPathFileName, verbose);
		try {
			compiler.parse();
		} catch (CompileError e) {
			compiler.handleCompileError(e);
			System.exit(1);
		}
		try {
			compiler.semanticAnalyze();
		} catch (CompileErrors e) {
			compiler.handleCompileErrors(e);
			System.exit(1);
		}
		compiler.compile();
	}
}
